{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "ioaprt5q5US7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "yCl0eTNH5RS3"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# Classificazione di testo con testo pre-elaborato: Recensioni di film"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hKY4XMc9o8iB"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/text_classification\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Visualizza su TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/it/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Esegui in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/it/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Visualizza il sorgente su GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/it/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Scarica il notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BYzaKBe8YXg0"
      },
      "source": [
        "Note: La nostra comunità di Tensorflow ha tradotto questi documenti. Poichè queste traduzioni sono *best-effort*, non è garantito che rispecchino in maniera precisa e aggiornata la [documentazione ufficiale in inglese](https://www.tensorflow.org/?hl=en). \n",
        "Se avete suggerimenti per migliorare questa traduzione, mandate per favore una pull request al repository Github [tensorflow/docs](https://github.com/tensorflow/docs). \n",
        "Per proporsi come volontari alla scrittura o alla review delle traduzioni della comunità contattate la \n",
        "[mailing list docs@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Eg62Pmz3o83v"
      },
      "source": [
        "Questo notebook classifica recensioni di film come *positive* o *negative* usando il testo delle revisioni. Questo è un esempio di classificazione *binaria*—o a due classi, un importante tipo di problema di machine learning largamente applicabile.\n",
        "\n",
        "Useremo il [dataset IMDB](https://www.tensorflow.org/datasets/catalog/imdb_reviews) che contiene il testo di 50.000 recensioni di film dall'[Internet Movie Database](https://www.imdb.com/). Esse sono divise in 25,000 recensioni per l'addestramento e 25,000 revisioni per la verifica. Gli insiemi di addestramento e verifica sono *bilanciati*, nel senso che essi contengono un eguale numero di recensioni positive e negative.\n",
        "\n",
        "Questo notebook usa [tf.keras](https://www.tensorflow.org/guide/keras), una API di alto livello per costruire e addestrare modelli in TensorFlow. Per un tutorial più avanzato di classificazione del testo che usa `tf.keras`, vedere la [MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8vdyFn79gt1L"
      },
      "source": [
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Nh0KjNGMWNlL"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8RZOuS9LWQvv"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  # %tensorflow_version only exists in Colab.\n",
        "  %tensorflow_version 2.x\n",
        "  !pip install tf-nightly\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2ew7HTbPpCJH"
      },
      "outputs": [],
      "source": [
        "from tensorflow import keras\n",
        "\n",
        "!pip install tensorflow-datasets\n",
        "import tensorflow_datasets as tfds\n",
        "tfds.disable_progress_bar()\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "print(tf.__version__)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iAsKG535pHep"
      },
      "source": [
        "<a id=\"download\"></a>\n",
        "\n",
        "## Scarichiamo il dataset IMDB\n",
        "\n",
        "Il dataset di recensioni di film IMDB viene compattato in `tfds`. Esso è stato già pre-elaborato in modo che le recensioni (sequenze di parole) sono state convertite in sequenze di interi, ove ciascun intero rappresenta una particolare parola in un vocabolario.\n",
        "\n",
        "Il codice che segue scarica il dataset IMDB sulla vostra macchina (o usa una copia locale se lo avete scaricato in precedenza):\n",
        "\n",
        "Per codificare il vostro testo vedere il [tutorial sul caricamento di testo](../load_data/text.ipynb)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wbIQ2wSeXSme"
      },
      "outputs": [],
      "source": [
        "(train_data, test_data), info = tfds.load(\n",
        "    # Use the version pre-encoded with an ~8k vocabulary.\n",
        "    'imdb_reviews/subwords8k', \n",
        "    # Return the train/test datasets as a tuple.\n",
        "    split = (tfds.Split.TRAIN, tfds.Split.TEST),\n",
        "    # Return (example, label) pairs from the dataset (instead of a dictionary).\n",
        "    as_supervised=True,\n",
        "    # Also return the `info` structure. \n",
        "    with_info=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qvA8HYDJj8OU"
      },
      "source": [
        "<a id=\"encoder\"></a>\n",
        "\n",
        "## Proviamo il codificatore\n",
        "\n",
        " Il dataset `info` include il codificatore di testo (un `tfds.features.text.SubwordTextEncoder`)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EplYp5pNnW1S"
      },
      "outputs": [],
      "source": [
        "encoder = info.features['text'].encoder"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "e7ACuHM5hFp3"
      },
      "outputs": [],
      "source": [
        "print ('Vocabulary size: {}'.format(encoder.vocab_size))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tAfGg8YRe6fu"
      },
      "source": [
        "Questo codificatore di testo codifica reversibilmente ogni stringa:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Bq6xDmf2SAs-"
      },
      "outputs": [],
      "source": [
        "sample_string = 'Hello TensorFlow.'\n",
        "\n",
        "encoded_string = encoder.encode(sample_string)\n",
        "print ('Encoded string is {}'.format(encoded_string))\n",
        "\n",
        "original_string = encoder.decode(encoded_string)\n",
        "print ('The original string: \"{}\"'.format(original_string))\n",
        "\n",
        "assert original_string == sample_string"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TbhM970AVA8w"
      },
      "source": [
        "Il codificatore codifica la stringa spezzandola in sotto-parole o caratteri se la parola non è presente nel suo vocabolario. In questo modo, più una stringa somiglia al dataset, più corta sarà la rappresentazione codificata."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GUIRWSO8yxT5"
      },
      "outputs": [],
      "source": [
        "for ts in encoded_string:\n",
        "  print ('{} ----> {}'.format(ts, encoder.decode([ts])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l50X3GfjpU4r"
      },
      "source": [
        "## Esploriamo i dati\n",
        "\n",
        "Prendiamoci un momento per capire il formato dei dati. Il dataset è pre-elaborato: ogni esempio è un vettore di interi che rappresenta le parole della recensione del film. \n",
        "\n",
        "I testi delle recensioni sono stati convertiti in interi, dove ciascun intero rappresenta un particolare frammento di parola nel vocabolario. \n",
        "\n",
        "Ogni etichetta è un valore intero tra 0 e 1, dove 0 è una recensione negativa, e 1 una recensione positiva.\n",
        "\n",
        "Qui ciò a cui somiglia la prima recensione:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cxnWQJijdGA1"
      },
      "outputs": [],
      "source": [
        "for train_example, train_label in train_data.take(1):\n",
        "  print('Encoded text:', train_example[:10].numpy())\n",
        "  print('Label:', train_label.numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wy0v9Hs4v41q"
      },
      "source": [
        "La struttura `info` contiene il codificatore/decodificatore. Il decodificatore può essere usato per recuperare il testo originale:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "34VUXtgxsVpf"
      },
      "outputs": [],
      "source": [
        "encoder.decode(train_example)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qJmTiO0IYAjm"
      },
      "source": [
        "## Prepariamo i dati per l'addestramento\n",
        "\n",
        "Vorrete creare lotti di dati di addestramento per il vostro modello. Le recensioni sono tutte di lunghezza diversa, così usiamo `padded_batch` per riempire di zeri le sequenze durante la suddivisione in lotti:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SDRI_s_tX1Hk"
      },
      "outputs": [],
      "source": [
        "BUFFER_SIZE = 1000\n",
        "\n",
        "train_batches = (\n",
        "    train_data\n",
        "    .shuffle(BUFFER_SIZE)\n",
        "    .padded_batch(32))\n",
        "\n",
        "test_batches = (\n",
        "    test_data\n",
        "    .padded_batch(32))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9D9pIr0JwvRl"
      },
      "source": [
        "Ogni lotto avrà una forma del tipo `(batch_size, sequence_length)` e dato che il riempimento è dinamico, ogni lotto avrà una lunghezza diversa:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sXXne4DreQfv"
      },
      "outputs": [],
      "source": [
        "for example_batch, label_batch in train_batches.take(2):\n",
        "  print(\"Batch shape:\", example_batch.shape)\n",
        "  print(\"label shape:\", label_batch.shape)\n",
        "  "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LLC02j2g-llC"
      },
      "source": [
        "## Costruiamo il modello\n",
        "\n",
        "La rete neurale viene creata impilando livelli—ciò richiede due decisioni architetturali principali:\n",
        "\n",
        "* Quanti livelli usare nel modello?\n",
        "* Quante *unità nascoste* usare in ciascun livello?\n",
        "\n",
        "In questo esempio, i dati di input sono costituiti da un vettore di parole-indici. Le etichette da prevedere sono 0 oppure 1. Costruiamo un modello in stile \"Continuous bag-of-words\" per questo problema:\n",
        "\n",
        "Attenzione: Questo modello non usa la mascheratura, così il riempimento di zeri viene utilizzato come parte dell'input, così la lunghezza del riempimento può influire sull'output.  Per evitare ciò, vedere la [guida al riempimento e mascheramento](../../guide/keras/masking_and_padding)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xpKOoWgu-llD"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential([\n",
        "  keras.layers.Embedding(encoder.vocab_size, 16),\n",
        "  keras.layers.GlobalAveragePooling1D(),\n",
        "  keras.layers.Dense(1)])\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "I livelli sono impilati in sequenza per implementare il classificatore:\n",
        "\n",
        "1. Il primo livello è un livello `Incorporamento`. Questo livello prende il vocabolario codificato in interi e guarda il vettore di incorporamento per ogni parola-indice. Questi vettori sono assimilati durante l'addestramento del modello. I vettori aggiungono una dimensione al vettore di output. Le dimensioni risultanti sono: `(batch, sequence, embedding)`.\n",
        "2. Successivamente, un livello `GlobalAveragePooling1D` restituisce in output un vettore di lunghezza fissa per ogni esempio mediando sulle dimensioni della sequenza. Ciò permette al modello di gestire input di lunghezza variabile, nel modo più semplice possibile.\n",
        "3. Questo vettore di output a lunghezza fissa viene passato attraverso un livello completamente connesso (`Denso`) con 16 unità nascoste.\n",
        "4. L'ultimo livello è connesso densamente ed ha un solo nodo di output. Usando la funzione di attivazione `sigmoid`, questo valore è un decimale tra 0 e 1, che rappresenta una probabilità, o un livello di confidenza."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0XMwnDOp-llH"
      },
      "source": [
        "### Unità nascoste\n",
        "\n",
        "Il modello di cui sopra ha due livelli intermedi o \"nascosti\", tra l'input e l'output. Il numero di output (unità, nodi o neuroni) è la dimensione dello spazio di rappresentazione del livello. In altre parole, l'ammontare della libertà di cui dispone la rete quando durante l'apprendimento di una rappresentazione interna.\n",
        "\n",
        "Se un modello ha più di un'unità nascosta (uno spazio di rappresentazione dimensionale più grande), e/o più livelli, allora la rete può apprendere rappresentazioni più complesse. Comunque, ciò rende la rete computazionalmente più costosa e può condurre all'apprendimento di pattern indesiderati—pattern che aumentano le prestazioni sui dati di addestramento ma non sui dati di test. Questo (fenomeno n.d.r.) viene chiamato *overfitting* (sovradattamento n.d.t.), e verrà esplorato in seguito."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### Funzione obiettivo e ottimizzatore\n",
        "\n",
        "Un modello, per l'addestramento, ha bisogno di una funzione obiettivo e di un ottimizzatore. Essendo questo un problema di classificazione binaria e l'output del modello una probabilità (un livello a unità singola con un'attivazione sigmoid), useremo la funzione obiettivo `binary_crossentropy`.\n",
        "\n",
        "Questa non è l'unica scelta possibile per una funzione obiettivo, potreste, per esempio, scegliere la `mean_squared_error`. In generale, però, `binary_crossentropy` è migliore per gestire probabilità—essa misura la \"distanza\" tra distribuzioni di probabilità o, nel nostro caso, tra la distribuzione dei dati reali e le previsioni.\n",
        "\n",
        "Nel seguito, quando esploreremo i problemi di regressione (diciamo, per prevedere il prezzo di una casa), vedremo come usare un'altra funzione obiettivo chiamata scarto quadratico medio.\n",
        "\n",
        "Adesso, configuriamo il modello per usare un ottimizzatore ed una funzione obiettivo:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Mr0GP-cQ-llN"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss=tf.losses.BinaryCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "35jv_fzP-llU"
      },
      "source": [
        "## Addestriamo il modello\n",
        "\n",
        "Addestrare il modello passando l'oggetto `Dataset` alla funzione di allenamento del modello. Impostare il numero di epoche."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tXSGrjWZ-llW"
      },
      "outputs": [],
      "source": [
        "history = model.fit(train_batches,\n",
        "                    epochs=10,\n",
        "                    validation_data=test_batches,\n",
        "                    validation_steps=30)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "## Valutiamo il modello\n",
        "\n",
        "E andiamo a vedere come si comporta il modello. Saranno restituiti due valori. loss (Perdita n.d.t.) (un numero che rappresenta il nostro errore, per cui valori piccoli sono migliori), e accuracy (accuratezza n.d.t)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zOMKywn4zReN"
      },
      "outputs": [],
      "source": [
        "loss, accuracy = model.evaluate(test_batches)\n",
        "\n",
        "print(\"Loss: \", loss)\n",
        "print(\"Accuracy: \", accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "Questo approccio abbastanza ingenuo raggiunge un'accuratezza di circa l'87%. Con approcci più avanzati, il modello potrebbe avvicinarsi al 95%."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5KggXVeL-llZ"
      },
      "source": [
        "## Creiamo un grafico di accuratezza e obiettivo nel tempo\n",
        "\n",
        "`model.fit()` restituisce un oggetto `History` che contiene un registro con tutto ciò che è accaduto durante l'addestramento:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VcvSXvhp-llb"
      },
      "outputs": [],
      "source": [
        "history_dict = history.history\n",
        "history_dict.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nRKsqL40-lle"
      },
      "source": [
        "Ci sono quattro sezioni: una per ogni metrica monitorata durante l'addestramento e la validazione. Possiamo usare queste per tracciare il confronto tra l'obiettivo in addestramento e in validazione, così come l'accuratezza in addestramento e validazione:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nGoYf2Js-lle"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "acc = history_dict['accuracy']\n",
        "val_acc = history_dict['val_accuracy']\n",
        "loss = history_dict['loss']\n",
        "val_loss = history_dict['val_loss']\n",
        "\n",
        "epochs = range(1, len(acc) + 1)\n",
        "\n",
        "# \"bo\" is for \"blue dot\"\n",
        "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
        "# b is for \"solid blue line\"\n",
        "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
        "plt.title('Training and validation loss')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6hXx-xOv-llh"
      },
      "outputs": [],
      "source": [
        "plt.clf()   # clear figure\n",
        "\n",
        "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
        "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
        "plt.title('Training and validation accuracy')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oFEmZ5zq-llk"
      },
      "source": [
        "In questo grafico, i punti rappresentano la perita e l'accuratezza in addestramento, mentre le linee continue sono l'obiettivo e l'accuratezza in validazione.\n",
        "\n",
        "Notate che l'obiettivo in addestramento *decresce* con le epoche e l'accuratezza *cresce* con le epoche. Questo è quello che ci si attende quando si usa un'ottimizzazione a gradiente discendente—esso dovrebbe minimizzare la quantità obiettivo ad ogni iterazione.\n",
        "\n",
        "Questo non accade per obiettivo e accuratezza in validazione—esse sembrano avere un picco dopo circa venti epoche. Questo è un esempio di sovradattamento: il modello ha prestazioni migliori sui dati di addestramento che su dati che non ha mai visto prima. Dopo questo punto, il modello sovra-ottimizza ed impara rappresentazioni *specifiche* dei dati di addestramento che non *generalizzano* sui dati di test.\n",
        "\n",
        "Per questo caso particolare, non possiamo prevenire il sovradattamento fermando semplicemente l'addestramento dopo più o meno venti epoche. Nel seguito, vedremo come farlo automaticamente con una callback."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "text_classification.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
